package main.model.island;

import java.io.File;

import main.model.critters.Critter;
import main.model.island.eventprocessor.IslandEventProcessorImp;
import main.model.island.grid.Grid;
import main.model.island.grid.GridGrass;
import main.model.island.grid.GridSand;
import main.model.island.grid.GridType;


public class ArrayIsland extends IslandEventProcessorImp implements IslandInterface {
	
	private static final int ISLANDMIN = 10;
    private static final int ISLANDMAX = 50;
    private static final int ISLANDINIT = 10; 
	
	private int xsize;
	private int ysize;
	
	private Grid[][] islandGrid;
	//private CritterCensus census;

	private File currentIsland = null;
	
	public boolean isAccessible(int x, int y){
		return islandGrid[x][y].isAccessible();
	}

	public ArrayIsland(){
		this(ISLANDMIN,ISLANDMIN);
	}
	
	public ArrayIsland(int xsize, int ysize){
		setSize(xsize,ysize);
		blankIsland();
	}
	
	public void randomize(){
		overWriteIsland(RandomIsland.createRandomIsland(xsize, ysize));
	}
	
	public void overWriteIsland(IslandInterface newIsland){
		this.xsize = newIsland.getXSize();
		this.ysize = newIsland.getYSize();
		
		Grid[][] islandGridTemp = new Grid[xsize][ysize];
		
		for(int x = 0; x < xsize ; x++){
			for(int y = 0; y < ysize; y++){
				islandGridTemp[x][y] = newIsland.getGrid(x, y);
			}
		}
		islandGrid = islandGridTemp;
		fireChangeEvent();
	}
	
	public void clear(){
		blankIsland();
	}
	
	public void setSize(int xsize,int ysize){
		if(xsize<ISLANDMIN){
			setSize(ISLANDMIN,ysize);
			return;
		}else if(xsize>ISLANDMAX){
			setSize(ISLANDMAX,ysize);
            return;
		}else if(ysize<ISLANDMIN){
			setSize(xsize,ISLANDMIN);
			return;
		}else if(ysize>ISLANDMAX){
			setSize(xsize,ISLANDMAX);
            return;
		}else{
			if(islandGrid == null){
				this.ysize = ysize;
				this.xsize = xsize;
				blankIsland();
			}else{
				Grid[][] islandGridTemp = new Grid[xsize][ysize];
				
				for(int x = 0; x < xsize ; x++){
					for(int y = 0; y < ysize; y++){
						if(this.xsize > x && this.ysize > y){
							islandGridTemp[x][y]  = islandGrid[x][y];
						}else{
							islandGridTemp[x][y] = new GridSand();
						}
					}
				}
				islandGrid = islandGridTemp;
				
				this.ysize = ysize;
				this.xsize = xsize;
				fireChangeEvent();
			}
		}
	}
	
	/**
	 * Set a specified x,y co-ordinate to be sand.
	 * 
	 * 
	 * @param x the x coordinate of the tree
	 * @param y the y coordinate of the tree
	 * @param g the grid to place
	 */
	private void setGridSand(int x, int y, Grid g){
		
		//Is it a valid location
		if(x>=xsize || y>=ysize || x<0 || y<0){
			return;
		}
		
		if(getGridType(x+1, y+1) == GridType.TREE) 		{setGrid(x, y , new GridGrass());}
		else if(getGridType(x, y+1) == GridType.TREE) 	{setGrid(x, y , new GridGrass());}
		else if(getGridType(x-1, y+1) == GridType.TREE)	{setGrid(x, y , new GridGrass());}
		else if(getGridType(x+1, y) == GridType.TREE) 	{setGrid(x, y , new GridGrass());}
		else if(getGridType(x-1, y) == GridType.TREE) 	{setGrid(x, y , new GridGrass());}
		else if(getGridType(x+1, y-1) == GridType.TREE)	{setGrid(x, y , new GridGrass());}
		else if(getGridType(x, y-1) == GridType.TREE) 	{setGrid(x, y , new GridGrass());}
		else if(getGridType(x-1, y-1) == GridType.TREE)	{setGrid(x, y , new GridGrass());}
		else{
			islandGrid[x][y] = g;
			fireChangeEvent();
		}
	}

	/**
	 * Set a specified x,y co-ordinate to be a tree.
	 * 
	 * First, a check is made to confirm that the space is a compatible square.
	 * Tress may only be placed in a checker-board formation 
	 * where the parity of the co-ordinates matches.
	 * If this check fails, the process is silently aborted.
	 * 
	 * Secondly, the squares surrounding the tree (provided they themselves are not trees)
	 * are set to grass. Checks are also made to avoid array out of bounds exceptions.
	 * 
	 * @param x the x coordinate of the tree
	 * @param y the y coordinate of the tree
	 */
	private void setGridTree(int x, int y, Grid g){
		
		//Is it a valid location
		if(x>=xsize || y>=ysize || x<0 || y<0){
			return;
		}
		
		//Is it a matching parity
		if ((x & 1) == 0 && (y & 1) == 0){
			islandGrid[x][y] = g;
			fireChangeEvent();
		}else if((x & 1) == 1 && (y & 1) == 1){
			islandGrid[x][y] = g;
			fireChangeEvent();
		}else{
			return;
		}
		
		if(getGrid(x+1, y) != null)	{setGrid(x+1, y, new GridGrass());}
		if(getGrid(x, y+1) != null)	{setGrid(x, y+1, new GridGrass());}
		if(getGrid(x-1, y) != null)	{setGrid(x-1, y, new GridGrass());}
		if(getGrid(x, y-1) != null)	{setGrid(x, y-1, new GridGrass());}
		
		if(getGridType(x+1, y+1) != GridType.TREE){
			setGrid(x+1, y+1, new GridGrass());
		}
		if(getGridType(x-1, y+1) != GridType.TREE){
			setGrid(x-1, y+1, new GridGrass());
		}
		if(getGridType(x+1, y-1) != GridType.TREE){
			setGrid(x+1, y-1, new GridGrass());
		}
		if(getGridType(x-1, y-1) != GridType.TREE){
			setGrid(x-1, y-1, new GridGrass());
		}
	}
	
	private void setGridGrass(int x, int y, Grid g){
		if(x>=xsize || y>=ysize || x<0 || y<0){
			return;
		}
		else{
			islandGrid[x][y] = g;
			fireChangeEvent();
		}
	}
	
	private void blankIsland(){
		islandGrid = new Grid[this.xsize][this.ysize];
		for(int x = 0; x < this.xsize ; x++){
			for(int y = 0; y < this.ysize; y++){
				islandGrid[x][y] = new GridSand();
			}
		}
		fireChangeEvent();
	}
	
	public int getXSize() {return xsize;}
	public int getYSize() {return ysize;}

	public GridType getGridType(int x, int y){
		if(x>=xsize || y>=ysize || x<0 || y<0){
			return null;
		}
		return islandGrid[x][y].getType();
	}	
	public Grid getGrid(int x, int y) {
		//Is it a valid location
		if(x>=xsize || y>=ysize || x<0 || y<0){
			return null;
		}
		return islandGrid[x][y];
	}

	public void setGrid(int x, int y, Grid g) {
		switch(g.getType()){
			case SAND:	setGridSand(x, y, g);	break;
			case GRASS:	setGridGrass(x, y, g);	break;
			case TREE:	setGridTree(x, y, g);	break;
		}
	}

	public int getMinSize() {return ISLANDMIN;}
	public int getMaxSize() {return ISLANDMAX;}
	public int getDefaultSize() {return ISLANDINIT;}

	@Override
	public File loadedIsland() {
		return loadedIsland();
	}
}
